﻿using System;
using System.Windows;

namespace Utility3D
{
    public class LineStroke : Stroke
    {
        public override Point? NearestPointOnSurface(DrawingSurface drawingSurface, Point point)
        {
            var startNullable = Start.GetOnSurface(drawingSurface);
            var endNullable = End.GetOnSurface(drawingSurface);

            if (startNullable == null || endNullable == null) return null;

            var start = startNullable.Value;
            var end = endNullable.Value;

            if (end.Y == start.Y)
            {
                var maxX = Math.Max(end.X, start.X);
                var minX = Math.Min(end.X, start.X);
                if (point.X > maxX)
                    return new Point {X = maxX, Y = start.X == maxX ? start.Y : end.Y};
                if (point.X < minX)
                    return new Point {X = minX, Y = start.X == minX ? start.Y : end.Y};
                return new Point {X = point.X, Y = start.Y};
            }

            var maxY = Math.Max(end.Y, start.Y);
            var minY = Math.Min(end.Y, start.Y);
            if (end.X == start.X)
            {
                if (point.Y > maxY)
                    return new Point {X = maxY == start.Y ? start.X : end.X, Y = maxY};
                if (point.Y < minY)
                    return new Point {X = maxY == start.Y ? start.X : end.Y, Y = minY};
                return new Point {X = start.X, Y = point.Y};
            }

            var thisSlope = (end.Y - start.Y)/(end.X - start.X);
            var mIntersect = -1/thisSlope;
            var bIntersect = point.Y - mIntersect*point.X;
            var mThis = thisSlope;
            var bThis = start.Y - mThis*start.X;

            var xIntercept = (bThis - bIntersect)/(mIntersect - mThis);
            var yIntercept = mThis*xIntercept + bThis;

            if (yIntercept > maxY)
                return new Point {X = maxY == start.Y ? start.X : end.X, Y = maxY};
            if (yIntercept < minY)
                return new Point {X = minY == start.Y ? start.X : end.X, Y = minY};

            return new Point {X = xIntercept, Y = yIntercept};
        }

        public override IPointRef BreakAtPoint(DrawingSurface drawingSurface, Point point)
        {
            var newPoint = drawingSurface.WorldModel.Collections.AddPoint(drawingSurface, point);

            drawingSurface.WorldModel.Collections.AddStroke<LineStroke>(newPoint, End, stroke => stroke.Shape = Shape);

            End = newPoint;

            return newPoint;
        }
    }
}